﻿/**
 * Licensed under the MIT License
 *
 * Copyright (c) 2010 Alexandre Croiseaux
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 * this software and associated documentation files (the "Software"), to deal in
 * the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
 * the Software, and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 *
 */
package marcel.core
{
	import flash.display.*;
	import flash.events.*;
	import flash.system.Capabilities;
	import marcel.controller.FrontController;
	import marcel.debug.*;
	import marcel.debug.loggers.*;
	import marcel.debug.tools.DebugInfos;
	import marcel.display.UI;
	import marcel.display.View;
	import marcel.external.browser.FlashVars;
	
	

	/**
	* Dispatched when the current swf file is fully loaded.
	* @eventType flash.events.Event.COMPLETE
	*/
	[Event(name="complete", type="flash.events.Event")]
	
	/**
	 * Plugin class used as a swf root class
	 * @author Alexandre Croiseaux
	 */
	public class Plugin extends View
	{
		//--------------------------------------------------------------------------
		//
		//  Private vars
		//
		//--------------------------------------------------------------------------
		private static var _plugin:Plugin;
		
		//--------------------------------------------------------------------------
		//
		//  Public vars
		//
		//--------------------------------------------------------------------------
		/**
		 * The Config instance used by this plugin to load and parse the xml config file
		 */
		public var config:Config;
		/**
		 * The AssetManager instance used by this plugin to load and store external assets
		 */
		public var assetManager:AssetManager;
		/**
		 * The StateManager instance used by this plugin to change the state, by opening and closing UIs as specified in the xml config file
		 */
		public var stateManager:StateManager;
		/**
		 * The flashVars instance used by this plugin to parse the swf flashvars passed by the HTML page
		 */
		public var flashVars:FlashVars;
		/**
		 * The FrontController instance listening to the current plugin
		 */
		public var frontController:FrontController;
		/**
		 * The sprite instance used by this plugin as a container for the different UIs. UIs can be reparented in other DisplayObjectContainer if needed.
		 */
		public var uisContainer:Sprite;

		//--------------------------------------------------------------------------
		//
		//  Constructor
		//
		//--------------------------------------------------------------------------
		/**
		 * Constructs a new Plugin instance, setting the root property to 'this' if it's undefined
		 */
		public function Plugin()
		{
			addEventListener(Event.ADDED_TO_STAGE, init);
		}
		
		
		//--------------------------------------------------------------------------
		//
		//  Public methods
		//
		//--------------------------------------------------------------------------
		/**
		 * Returns the plugin instance of a application, defined the first time a Plugin instance is created.
		 */
		public static function get plugin():Plugin
		{
			if (_plugin == null) throw new Error("Plugin.plugin = null.\nSWF main class must inherit from marcel.structures.Plugin");
			return _plugin;
		}
		
		/**
		 * Returns the URI of the current SWF file
		 * @return	a string representing the http URI
		 */
		public function getURI():String { return unescape(loaderInfo.url); }
		
		/**
		 * Returns the name of the current SWF file
		 * @return	a string representing the file name
		 */
		public function getSWFFileName():String
		{
			return getURI().split("#").shift().split("?").shift().split("/").pop().split("\\").pop();
		}
		
		/**
		 * Returns the Flash Player version playing the current SWF file
		 * @return	a string representing Capabilities.version
		 */
		public function getPlayerVersion():String { return Capabilities.version; }
		
		/**
		 * Check if the player is running in a browser or in standalone mode
		 * @return	true if the player is running in standalone, false if it's reunning in a browser
		 */
		public function isStandAlonePlayer():Boolean { return Capabilities.playerType == "StandAlone" || Capabilities.playerType == "External"; }
		
		
		/**
		 * Check if the current file is running in a local env (file uri starting with 'file:')
		 * @return	true if the player is running in a local env, false otherwise (http, ftp, https...)
		 */
		public function isLocalServed():Boolean { return getURI().substr(0, 4) == "file"; }
		
		
		/**
		 * Check if the swf is compiled in debug mode
		 * @return	true if the swf is compiled in debug mode, false if it's compiled in release mode
		 */
		public function isCompiledInDebugMode():Boolean
		{
			try
			{
				var hasLineNumbers:Boolean = new Error().getStackTrace().search(/:[0-9]+]$/m) > -1;
				return hasLineNumbers;
			}
			catch (e:Error) {}
			return false;
		}
		
		
		/**
		 * Locks the view from mouse interaction. Use this method during loading process.
		 * @param	locked true to lock the mouse interaction, false otherwise.
		 */
		public function lockView(locked:Boolean):void
		{
			mouseEnabled = !locked;
			mouseChildren = !locked;
		}
		
		/**
		 * Returns an UI instance if it's opened
		 * @param	id	The UI id.
		 * @return	en UI instance or null if no opened UI is found for this id.
		 */
		public function getUI(id:String):UI
		{
			return stateManager.getUI(id);
		}
		
		
		//--------------------------------------------------------------------------
		//
		//  Private methods
		//
		//--------------------------------------------------------------------------
		/**
		 * @private
		 */
		protected function init(e:Event):void
		{
			// init static root if needed
			if (_plugin == null)
			{
				_plugin = this;
				_plugin.stage.align = StageAlign.TOP_LEFT;
				_plugin.stage.scaleMode = StageScaleMode.NO_SCALE;
				loaderInfo.addEventListener(Event.INIT, onRootLoaded);
				loaderInfo.addEventListener(IOErrorEvent.IO_ERROR, onRootError);
			}
			
			// managers
			initManagers();
			
			// controller
			initController();
			
			// UIs container
			uisContainer = new Sprite();
			addChild(uisContainer);
			
			// Logging & debug
			if (isCompiledInDebugMode() || flashVars.getValue("enable_debug") == "true")
			{
				initDebug();
			}
			
			//not needed anymore
			removeEventListener(Event.ADDED_TO_STAGE, init);
			
			// Global error handling available in player >= 10.1
			if (loaderInfo.hasOwnProperty("uncaughtErrorEvents"))
			{
				IEventDispatcher(loaderInfo["uncaughtErrorEvents"]).addEventListener("uncaughtError", onUncaughtError);
			}
		}
		
		/**
		 * @private
		 */
		protected function initManagers():void
		{
			flashVars = new FlashVars(Plugin.plugin);
			assetManager = new AssetManager();
			stateManager = new StateManager(this);
			config = new Config(this);
			config.addEventListener(Event.COMPLETE, onConfigComplete);
		}
		
		/**
		 * @private
		 */
		protected function initController():void {
			frontController = new FrontController(this);
		}

		/**
		 * @private
		 */
		protected function initDebug():void
		{
			Logger.clientUri = getURI();
			Logger.addLogger(new TraceLogger());
			Logger.addLogger(new SWFConsole());
			Logger.addLogger(new AirLogger());
			Logger.addLogger(new MonsterLogger());
			Logger.addLogger(new SOSLogger());
			
			addChild(new DebugInfos());
		}
		
		//--------------------------------------------------------------------------
		//
		//  Events
		//
		//--------------------------------------------------------------------------
		private function onRootLoaded(evt:Event):void
		{
			config.build();
		}
		
		private function onConfigComplete(e:Event):void
		{
			dispatchEvent(new Event(Event.COMPLETE));
		}
		
		private function onRootError(e:IOErrorEvent):void {	}
		
		private function onUncaughtError(e:Event):void
		{
			debuge(e);
		}
	}
}